/*
 * Based on work by:
 * @author  James Gosling
 * @version 1.40, 12/01/05
 * @see     java.io.PipedInputStream
 * @see     java.io.PipedOutputStream
 *
 * Derivative work is Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph.pipes;

import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;

/**
 * An output pipe for {@code short} values. This pipe, the source, connects 
 * to a {@code ShortInputPipe}, the sink, for a sequential transmission of 
 * short ints. Code in one thread makes write calls to an instance of this 
 * class, where the data is sent to the sink. Code in another thread makes 
 * read calls to sink to get the short integers from a FIFO buffer.
 * 
 * @author Armando Blancas
 */
public class ShortOutputPipe implements Closeable, Flushable {

    private ShortInputPipe sink;

    /**
     * ShortOutputPipe default constructor.
     * 
     * Creates a {@code short} output pipe that is not yet connected to a 
     * {@code short} input pipe. It must be connected to an input pipe, 
     * either by the receiver or the sender, before being used. 
     */
    public ShortOutputPipe() {
    	// do nothing
    }
    
    /**
     * ShortOutputPipe constructor.
     * 
     * Creates a {@code short} output pipe connected to the specified input 
     * pipe. {@code short} values written to this pipe will then be 
     * available as input from {@code sink}.
     *
     * @param sink The input pipe to connect to.
     * @throws IOException If an I/O error occurs.
     */
    public ShortOutputPipe(final ShortInputPipe sink) throws IOException {
    	connect(sink);
    }
    
    /**
     * Connects to an input pipe.
     * 
     * Connects this {@code short} output pipe to the passed {@code pipe}
     * argument. If either the receiver or the passed pipe is already 
     * connected, an {@code IOException} is thrown.
     * <p>If {@code sink} is an unconnected input pipe and {@code source} is
     * an unconnected output pipe, they may be connected by either the call:
     * <pre>source.connect(sink)</pre>
     * or the call:
     * <pre>sink.connect(source)</pre>
     * The two calls have the same effect.
     *
     * @param pipe The piped input short stream to connect to.
     * @throws IOException If an I/O error occurs.
     */
    public synchronized void connect(ShortInputPipe pipe) throws IOException {
        if (pipe == null) {
            throw new NullPointerException();
        } else if (sink != null || pipe.connected) {
        	throw new IOException("Already connected");
        }
        sink = pipe;
        sink.in = -1;
        sink.out = 0;
        sink.connected = true;
    }

    /**
     * Writes the specified {@code short} to this output pipe. 
     *
     * @param s The {@code short} to be written.
     * @throws IOException If the pipe is <a href=#BROKEN> broken</a>,
     *		   {@link #connect(taskgraph.pipes.ShortInputPipe) unconnected},
     *		   closed, or if an I/O error occurs.
     */
    public void write(short s) throws IOException {
        if (sink == null) {
            throw new IOException("Pipe not connected");
        }
        sink.receive(s);
    }

    /**
     * Writes an array of shorts.
     * 
     * Writes {@code s.length} shorts from the specified {@code short} 
     * array to this output pipe. The general contract for {@code write(s)} 
     * is that it should have exactly the same effect as the call 
     * {@code write(s, 0, s.length)}.
     *
     * @param s The {@code short} data to write.
     * @throws IOException If an I/O error occurs.
     */
    public void write(short[] s) throws IOException {
    	write(s, 0, s.length);
    }

    /**
     * Writes an array of shorts.
     * 
     * Writes {@code len} short integers from the specified {@code short} array 
     * starting at offset {@code off} into this output pipe. 
     * This method blocks until all the short integers are written.
     *
     * @param s   The {@code short} data to write.
     * @param off The start offset in the data.
     * @param len The number of short integers to write.
     * @throws IOException If the pipe is <a href=#BROKEN> broken</a>,
     *         {@link #connect(taskgraph.pipes.ShortInputPipe) unconnected},
     *		   closed, or if an I/O error occurs.
     */
    public void write(short[] s, int off, int len) throws IOException {
        if (sink == null) {
            throw new IOException("Pipe not connected");
        } else if (s == null) {
        	throw new NullPointerException();
        } else if ((off < 0) || (off > s.length) || (len < 0) ||
		           ((off + len) > s.length) || ((off + len) < 0)) {
        	throw new IndexOutOfBoundsException();
        } else if (len == 0) {
        	return;
        } 
        sink.receive(s, off, len);
    }

    /**
     * Flushes this output pipe. It forces any buffered output short integers 
     * to be written out. This will notify any readers that characters are 
     * waiting in the pipe.
     *
     * @throws IOException If an I/O error occurs.
     */
    public synchronized void flush() throws IOException {
    	if (sink != null) {
    		synchronized (sink) {
                sink.notifyAll();
            }
    	}
    }

    /**
     * Closes this output pipe and releases any system resources. This pipe
     * may no longer be used for writing chacacters.
     */
    public void close() {
    	if (sink != null) {
    		sink.receivedLast();
    	}
    }

}
